参数

  当函数接受参数时,就必须指定以下内容:

  • 函数在其定义中指定接受的参数列表,以及这些参数的类型。
  • 在每个函数调用中提供匹配的实参列表。

  仔细阅读 C#规范会发现行參( parameter )和实参( argument )之间存在一些细微的区别:行參是函数定义的一部分,而实参则由调用代码传递给函数。但是,这两个术语通常被简单地称为参数,似乎没有人对此感到十分不满。

  示例代码如下所示,其中可以有任意数量的参数,每个参数都有一个类型和一个名称:

        static <returnType> <FunctionName>(<paramType> <paramName>, ... )
        {
            ...
            return <returnValue>;
        }

  参数用逗号分隔开。每个参数都在函数的代码中用作一个变量。例如,下面是一个简单的函数,带有两个 double 参数,并返回它们的乘积:

        static double Product (double param1, double param2)
        {
            return param1 * parma2;
        }

  下面看一个较复杂的示例。

  把下列代码添加到 Program.cs 中:

        class Program
        {
            static int MaxValue(int[] intArray)
            {
                int maxVal = intArray[0];
                for (int i = 1; i < intArray.Length; i++)
                {
                    if(intArray[i] > maxVal)
                        maxVal = intArray[i];
                }
                return maxVal;
            }

            static void Main(string[] args)
            {
                int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 };
                int maxVal = MaxValue(myArray);
                Console.WriteLine("The maximum value in myArray is {0}", maxVal);
                Console.ReadKey();
            }
        }

  示例的说明

  这段代码包含一个函数,它执行的任务就是本章引言中示例函数所完成的任务。该函数以一个整数数组作为参数,并返回该数组中的最大值。该函数的定义如下所示:

        static int MaxValue(int[] intArray)
        {
            int maxVal = intArray[0];
            for (int i = 1; i < intArray.Length; i++)
            {
                if (intArray[i] > maxVal)
                    maxVal = intArray[i];
            }
            return maxVal;
        }

  函数 MaxValue() 定义了一个参数,即 int 数组 intArray,它还有一个 int 类型的返回值。最大值的计算是很简单的。局部整型变量 maxVal 初始化为数组中的第一个值,然后把这个值与数组中后面的每个元素依次进行比较。如果一个元素的值比 maxVal 大,就用这个值代替当前的 maxVal 值。循环结束时,maxVal 就包含数组中的最大值,用 return 语句返回。

  Main() 中的代码声明并初始化一个简单的整数数组,用于 MaxValue() 函数:

        int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 };

  调用 MaxValue(),把一个值赋给 int 变量 maxVal

        int maxVal = MaxValue(myArray);

  接着,使用 Console.WriteLine() 把这个值写到屏幕上:

        Console.WriteLine("The maximum value in myArray is {0}", maxVal);

  1. 参数匹配

  在调用函数时,必须使提供的参数与函数定义中指定的参数完全匹配,这意味着要匹配参数的类型、个数和顺序。例如,下面的函数:

        static void MyFunction(string myString, double myDouble)
        {
            ...
        }

  不能使用下面的代码调用:

        MyFunction(2.6, "Hello");  ❌

  这里试图把一个 double 值作为第一个参数传递,把 string 值作为第二个参数传递,参数顺序与函数声明中定义的顺序不匹配。

  也不能使用下面的代码:

        MyFunction("Hello");  ❌

  这里仅传送一个 string 参数,而该函数需要两个参数。使用上述两个函数调用都会产生编译错误,因为编译器要求必须匹配所有函数的签名。

  使用函数的名称和参数来定义函数的签名。

  再回顾这个示例,MaxValue() 只能用于获取整数数组中的最大 int 值。如果用下面的代码替换 Main() 中的代码:

        static void Main(string[] args)
        {
            double[] myArray = { 1.3, 8.9, 3.3, 6.5, 2.7, 5.3 };
            double maxVal = MaxValue(myArray);  ❌
            Console.WriteLine("The maximum value in myArray is {0}", maxVal);
            Console.ReadKey();
        }

  就不能编译这段代码,因为参数类型是错误的。在本章后面的 “重载函数” 一节将介绍解决这个问题的一个有效技术。


  2. 参数数组

  C#允许为函数指定一个(只能指定一个)特殊参数,这个参数必须是函数定义中的最后一个参数,称为参数数组。参数数组允许用个数不定的参数调用函数,可使用 params 关键字定义它们。

  参数数组可以简化代码,因为在调用代码中不必传递数组,而是传递同类型的几个参数,这些参数会被放在可在函数中使用的一个数组中。

  定义使用参数数组的函数时,需要使用下列代码:

        static <returnType> <FunctionName>(<p1Type> <p1Name>, ..., params <type>[] <name>)
        {
            ...
            return <returnValue>;
        }

  使用下面的代码可以调用该函数。

        <FunctionName>(<p1>, ..., <val1>, <val2>, ...)

  其中 <val1><val2> 等都是 <type> 类型的值,用于初始化 <name> 数组。可以指定的参数个数几乎不受限制,但它们都必须是 <type> 类型。甚至根本不必指定参数。

  这一点使参数数组特别适合于函数提供一些额外的信息,供它们在处理过程中使用。例如,假定有一个函数 GetWord(),它的第一个参数是一个 string 值,并返回字符串中的第一个单词。

        string firstWord = GetWord("This is a sentence.");

  其中 firstWord 被赋予字符串 This

  可在 GetWord() 中添加一个 params 参数,以根据其索引选择另一个要返回的单词:

        string firstWord = GetWord("This is a sentence.", 2);

  假定第一个单词计数为 1,则 firstWord 就被赋予字符串 is

  也可以在第 3 个参数中限制返回的字符个数,同样通过 params 参数来实现:

        string firstWord = GetWord("This is a sentence.", 4, 3);

  此时 firstWord 被赋予字符串 sen

  下面的示例定义并使用带有 params 类型参数的函数。

  把下述代码添加到 Program.cs 中:

        class Program 
        { 
            static int SumVals(params int[] vals) 
            { 
                int sum = 0; 
                foreach (int val in vals) 
                { 
                    sum += val; 
                } 
                return sum; 
            }

           static void Main(string[] args) 
            { 
                int sum = SumVals(1, 5, 2, 9, 8); 
                Console.WriteLine("Summed Values = {0}", sum); 
                Console.ReadKey(); 
            } 
        }

  示例的说明

  这个示例用关键字 params 定义函数 sumVals(),该函数可以接受任意个 int 参数(但不接受其他类型的参数):

        static int SumVals(params int[] vals) { ... }

  这个函数对 vals 数组中的值进行迭代,将这些值加在一起,返回其结果。

  在 Main() 中,用 5 个整型参数调用函数 SumVals()

        int sum = SumVals(1, 5, 2, 9, 8);

  也可以用 0、1、2 或 100 个整型参数调用这个函数--参数的数量不受限制。

  C# 4 中引入了指定函数参数的新方式,包括用一种可读性更好的方式来包含可选参数。第 14 章将介绍这些方法,从该章中可以了解到自问世以来,C#语言发生了怎样的变迁。


  3. 引用参数和值参数

  本章迄今定义的所有函数都带有值参数。其含义是,在使用参数时,是把一个值传递给函数使用的一个变量。对函数中此变量的任何修改都不影响函数调用中指定的参数。例如,下面的函数使传递过来的参数值加倍,并显示出来:

        static void ShowDouble(int val) 
        { 
            val *= 2; 
            Console.WriteLine("val doubled = {0}", val); 
        }

  参数 val 在这个函数中被加倍,如果按一下方式调用它:

        int myNumber = 5; 
        Console.WriteLine("myNumber = {0}", myNumber); 
        ShowDouble(myNumber); 
        Console.WriteLine("myNumber = {0}", myNumber);

  输出到控制台上的文本如下所示:

        myNumber = 5 
        val doubled = 10

        myNumber = 5

  把 myNumber 作为一个参数,调用 ShowDouble() 并不影响 Main()myNumber 大的值,即使把 myNumber 赋值给 val 后将 val 加倍,myNumber 的值也不变。

  这很不错,但如果要改变 myNumber 的值,就会有问题。可以使用一个为 myNumber 返回新值的函数:

        static int DoubleNum(int val) 
        { 
            val *= 2; 
            return val; 
        }

  并使用下面的代码调用它:

        int myNumber = 5; 
        Console.WriteLine("myNumber = {0}", myNumber); 
        myNumber = DoubleNum(myNumber); 
        Console.WriteLine("myNumber = {0}", myNumber);

  但这段代码一点也不直观,且不能改变用作参数的多个变量值(因为函数只有一个返回值)。

  此时可以通过 “引用”传递参数。即函数处理的变量与函数调用中使用的变量相同,而不仅仅是值相同的变量。因此,对这个变量进行的任何改变都会影响用作参数的变量值。为此,只需使用 ref 关键字指定参数:

        static void ShowDouble(ref int val) 
        { 
            val *= 2; 
            Console.WriteLine("val doubled = {0}", val); 
        }

  在函数调用中再次指定它(这是必须的,因为 ref 参数是函数签名的一部分):

        int myNumber = 5; 
        Console.WriteLine("myNumber = {0}", myNumber); 
        ShowDouble(ref myNumber); 
        Console.WriteLine("myNumber = {0}", myNumber);

  输出到控制台的文本如下所示:

        myNumber = 5 
        val doubled = 10 
        myNumber = 10

  这次,myNumberShowDouble() 修改了。

  用作 ref 参数的变量有两个限制。首先,函数可能会改变引用参数的值,所以必须在函数调用中使用 “非常量” 变量。所以,下面的代码是非法的:

        const int myNumber = 5; 
        Console.WriteLine("myNumber = {0}", myNumber); 
        ShowDouble(ref myNumber); 
        Console.WriteLine("myNumber = {0}", myNumber);

  其次,必须使用初始化过的变量。C#不允许假定 ref 参数在使用它的函数中初始化,下面的代码也是非法的:

        int myNumber; ShowDouble(ref myNumber); ❌ 
        Console.WriteLine("myNumber = {0}", myNumber);

  4. 输出参数

  除了按引用传递值外,还可以使用 out 关键字,指定所给的参数是一个输出参数。 out 关键字的使用方式与 ref 关键字相同(在函数定义和函数调用中用作参数的修饰符)。实际上,它的执行方式与引用参数完全一样,因为在函数执行完毕后,该参数的值将返回给函数调用中使用的变量。但是,二者存在一些重要区别:

  • 把未赋值的变量用作 ref 参数是非法的,但可以把未赋值的变量用作 out 参数。* 另外,在函数使用 out 参数时,必须把它看成是尚未赋值。

  即调用代码可以把已赋值的变量用作 out 参数,但存储在该变量中的值会在函数执行时丢失。

  例如,考虑前面返回数组中最大值的 MaxValue() 函数,略微修改该函数,获取数组中最大值的元素索引。为简单起见,如果数组中有多个元素的值都是这个最大值,只提取第一个最大值的索引。为此,修改函数,添加一个 out 参数,如下所示:

        static int MaxValue(int[] intArray, out int maxIndex) 
        { 
            int maxVal = intArray[0]; 
            maxIndex = 0; 
            for (int i = 1; i < intArray.Length; i++) 
            { 
                if (intArray[i] > maxVal) 
                { 
                    maxVal = intArray[i]; 
                    maxIndex = i; 
                } 
            } 
            return maxVal; 
        }

  可以采用以下方式使用该函数:

        int[] myArray = { 1, 8, 3, 6, 2, 5 9, 3, 0, 2 }; 
        int maxIndex; 
        Console.WriteLine("The maximum value in myArray is {0}", MaxValue(myArray, out maxIndex)); 
        Console.WriteLine("The first occurrence of this value is at element {0}", maxIndex + 1);

  结果如下:

        The maximum value in myArray is 9 
        The first occurrence of this value is at element 7

  注意 ⚠️,必须在函数调用中使用 out 关键字,就像 ref 关键字一样。

  在屏幕上显示结果时,给返回的 maxIndex 的值加上 1。这样可以使索引更容易读懂,因此数组的第一个元素记为元素 1,而不是元素 0。

🔚